#include "codeeditor.h"
#include "projectmanager.h"
#include "../lua_parser/TokenParser.h"
#include "lualexer.h"
#include <QMenu>
#include <QFileInfo>
#include <Qsci/qscicommandset.h>

CodeEditor::CodeEditor(QWidget *parent)
    :QsciScintilla(parent)
{
    m_projectManager = NULL;
    m_forwardSearchDirection = true;
    m_hyperlinkedRangeStart = -1;

    m_completionList = new QListView(this);
    m_completionList->hide();

}

void CodeEditor::keyReleaseEvent(QKeyEvent *e)
{
    //TODO keybindings in settings
    /*if(e->modifiers().testFlag(Qt::ControlModifier) == true)
    {
        if(e->key() == 'c')
            copy();
        else if(e->key() == 'v')
            paste();
        else if(e->key() == 'x')
            cut();
        e->ignore();
        return;
    }*/
    //reset link highlighting
    if(m_hyperlinkedRangeStart >= 0 && e->modifiers().testFlag(Qt::ControlModifier) == false)
    {
        LuaLexer * luaLexer = (LuaLexer *)this->lexer();
        if(luaLexer)
        {
            luaLexer->setHyperlinkRange(-1, 0);
            luaLexer->styleText(m_hyperlinkedRangeStart, m_hyperlinkedRangeStart + m_hyperlinkedRangeLength);
            //SendScintilla(QsciScintilla::SCI_COLOURISE, 0, -1);
        }
        m_hyperlinkedRangeStart = -1;
    }    
    QsciScintilla::keyReleaseEvent(e);
}


static void extractRelevantParts(QString aWord, QStringList & aList)
{
    //for every item of aList replaces item with relevant section of item
    //for example for aWord = a.te
    //a.test.c -> test
    int leftDotIndex = aWord.lastIndexOf('.');
    int count = aList.size();
    for(int i = 0; i < count; i++)
    {
        QString suggestion = aList[i];
        if(suggestion.indexOf('.') < 0)
            continue;
        int rightDotIndex = suggestion.indexOf('.', aWord.length());
        if(rightDotIndex < 0 && leftDotIndex < 0)
            continue;
        else if(leftDotIndex > 0 && rightDotIndex > 0)
            aList[i] = suggestion.mid(leftDotIndex + 1, rightDotIndex - leftDotIndex - 1);
        else if(leftDotIndex > 0 && rightDotIndex < 0)
            aList[i] = suggestion.right(suggestion.length() - leftDotIndex - 1);
        else if(leftDotIndex < 0 && rightDotIndex > 0)
            aList[i] = suggestion.left(rightDotIndex);
    }
}

void CodeEditor::keyPressEvent(QKeyEvent *e)
{
    int key = e->key();    
    if(key == Qt::Key_Escape)
    {
        emit hideSearch();
        e->ignore();
        return;
        //return;
    }

    if(!m_projectManager)
    {
        QsciScintilla::keyPressEvent(e);
        return;
    }    
    //TODO keybindings in settings
    if(e->modifiers().testFlag(Qt::ControlModifier) == true)
    {
        if(e->key() == 'C' && hasSelectedText())
            copy();
        else if(e->key() == 'V')
            paste();
        else if(e->key() == 'X')
            cut();
        else if(e->key() == 'Z')
            undo();
        else if(e->key() == 'A')
            selectAll();
        else if (e->key() == 'F')
            emit searchRequested();
        e->ignore();
        return;
    }
    if(e->key() == Qt::Key_Tab && hasSelectedText())
    {
        indentSelection();
        e->accept();
        return;
    }
    if(e->key() == Qt::Key_Backtab)
    {
        unindentSelection();
        e->accept();
        return;
    }
    if(IS_WORD_SYMBOL(key) || key == '.')
    {
        QStringList list;
        int line = 0;
        int index = 0;
        getCursorPosition(&line, &index);
        int offset = positionFromLineIndex(line, index);
        m_wordOffset = m_projectManager->getSuggestions(list, offset, key);
        if(list.size() == 0)
        {
           QsciScintilla::keyPressEvent(e);
           return;
        }
        QString word = m_projectManager->getModifiedWord(offset, key);
        if(word.length() == 0)
            return;
        int dotIndex = word.lastIndexOf('.');
        if(dotIndex > 0)
            m_wordOffset = m_wordOffset - dotIndex - 1;
        extractRelevantParts(word, list);
        list.removeDuplicates();
        showUserList(1, list);
        insert(e->text());
        setCursorPosition(line, index + 1);
        return;
    }
    QsciScintilla::keyPressEvent(e);
}

void CodeEditor::setProjectManager(ProjectManager *aManager)
{
    m_projectManager = aManager;
}

int CodeEditor::getWordOffset()
{
    return m_wordOffset;
}

void CodeEditor::findAndScrollToWord(QString aWord)
{
    findFirst(aWord, false, false, false, true, true, 0, 0);
    m_forwardSearchDirection = true;
}

void CodeEditor::findNextMatch(QString aWord)
{
    if(!m_forwardSearchDirection)
    {
        //changing direction
        int lineFrom = 0;
        int lineTo = 0;
        int indexFrom = 0;
        int indexTo = 0;
        getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo);
        int position = positionFromLineIndex(lineFrom, indexFrom);
        position += aWord.length();
        lineIndexFromPosition(position, &lineFrom, &indexFrom);
        findFirst(aWord, false, false, false, true, true, lineFrom, indexFrom);
    }
    else
    {
        findNext();
    }
    m_forwardSearchDirection = true;
}

void CodeEditor::findPreviousMatch(QString aWord)
{
    if(m_forwardSearchDirection)
    {
        //changing direction
        int lineFrom = 0;
        int lineTo = 0;
        int indexFrom = 0;
        int indexTo = 0;
        getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo);
        //int position = positionFromLineIndex(lineFrom, indexFrom);
        //position += aWord.length();
        //lineIndexFromPosition(position, &lineFrom, &indexFrom);
        findFirst(aWord, false, false, false, true, false, lineFrom, indexFrom);
    }
    else
    {
        findNext();
    }
    m_forwardSearchDirection = false;
}

void CodeEditor::replaceAll(QString aOldText, QString aNewText)
{
    bool found = findFirst(aOldText, false, false, false, true, true, 0, 0);
    while(found)
    {
        replace(aNewText);
        found = findNext();
    }
}

void CodeEditor::goToOffset(int aOffset)
{
    int line = 0;
    int index = 0;
    lineIndexFromPosition(aOffset, &line, &index);
    setFirstVisibleLine(line - 5);
    setCursorPosition(line, index);

}

void CodeEditor::mouseMoveEvent (QMouseEvent * event)
{    
    int x = event->x();
    int y = event->y();
    int position = SendScintilla(QsciScintilla::SCI_POSITIONFROMPOINTCLOSE, x, y);
    //if(position < 0)
      //  return;
    int start = 0;
    int length = 0;
    bool result = false;
    if(position >= 0 && event->modifiers().testFlag(Qt::ControlModifier))
        result = m_projectManager->getWordBoundaries(position, start, length);
    if(result)
    {
        //qDebug("POS:%d -> %d %d\n", position, start, length);
        LuaLexer * luaLexer = (LuaLexer *)this->lexer();
        if(!luaLexer)
            return;
        if(m_hyperlinkedRangeStart >= 0)
        {
            //luaLexer->setHyperlinkRange(-1, 0);
            //luaLexer->styleText(m_hyperlinkedRangeStart, m_hyperlinkedRangeStart + m_hyperlinkedRangeLength);
            SendScintilla(QsciScintilla::SCI_COLOURISE, 0, -1);
        }

        //luaLexer->highlightLink(start, length);
        m_hyperlinkedRangeStart = start;
        m_hyperlinkedRangeLength = length;
        luaLexer->setHyperlinkRange(m_hyperlinkedRangeStart, m_hyperlinkedRangeLength);
        SendScintilla(QsciScintilla::SCI_COLOURISE, start, start + length);
    }
    else
    {
        if(m_hyperlinkedRangeStart > 0)
        {
            LuaLexer * luaLexer = (LuaLexer *)this->lexer();
            if(!luaLexer)
                return;
            luaLexer->styleText(m_hyperlinkedRangeStart, m_hyperlinkedRangeStart + m_hyperlinkedRangeLength);
            m_hyperlinkedRangeStart = -1;
            luaLexer->setHyperlinkRange(m_hyperlinkedRangeStart, m_hyperlinkedRangeLength);
        }
    }
    QsciScintilla::mouseMoveEvent(event);
}


void CodeEditor::parseGlobalLink(QString aGlobalLink, QStringList & aComponents)
{
    QStringList parts = aGlobalLink.split(":");
    //skip "global"
    aComponents.clear();
    aComponents.push_back(parts[1]);//offset
    aComponents.push_back(parts[2]);//filename
    aComponents.push_back(parts[3]);//text

}

QString CodeEditor::getGlobalLinkByFormattedLink(QString aFormattedLink)
{
    if(m_navigationListContent.contains(aFormattedLink))
        return m_navigationListContent[aFormattedLink];
    return "";
}

void CodeEditor::mouseReleaseEvent ( QMouseEvent * event )
{
    int x = event->x();
    int y = event->y();
    int position = SendScintilla(QsciScintilla::SCI_POSITIONFROMPOINTCLOSE, x, y);
    if(position < 0 || event->modifiers().testFlag(Qt::ControlModifier) == false)
    {
        QsciScintilla::mouseReleaseEvent(event);
        return;
    }
    QsciScintilla::mouseReleaseEvent(event);
    QStringList links;
    bool result = m_projectManager->getClickedLink(position, links);
    if(result)
    {
         if(links.count() > 1)
         {
             m_navigationListContent.clear();
             QStringList formattedLinks;
             QStringList components;
             for(int i = 0; i < links.count(); i++)
             {
                 parseGlobalLink(links[i], components);
                 QString formattedLink = components[2] + " - " + QFileInfo(components[1]).fileName();
                 m_navigationListContent[formattedLink] = links[i];
                 formattedLinks.push_back(formattedLink);
                 showUserList(2, formattedLinks);//TODO user id constants
             }
         }
         else
             emit linkClicked(links[0]);
    }
}


void CodeEditor::scrollContentsBy( int dx, int dy )
{
    QsciScintilla::scrollContentsBy(dx, dy);
    emit scrolled(dy);
}




void CodeEditor::indentSelection()
{
    int lineFrom = 0;
    int lineTo = 0;
    int indexFrom = 0;
    int indexTo = 0;
    getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo);
    //indent all lines
    for(int i = lineFrom; i < lineTo + 1; i++)
        indent(i);
}

void CodeEditor::unindentSelection()
{
    int lineFrom = 0;
    int lineTo = 0;
    int indexFrom = 0;
    int indexTo = 0;
    getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo);
    //indent all lines
    for(int i = lineFrom; i < lineTo + 1; i++)
        unindent(i);
}


int CodeEditor::getCursorOffset()
{
    int line = 0;
    int index = 0;
    getCursorPosition(&line, &index);
    return positionFromLineIndex(line, index);
}

void CodeEditor::initFromSettings()
{
   setCaretLineVisible(
                SettingsManager::Instance()->getSetting(
                    "editor.caretlinevisible").getBoolValue());
    //line color
    setCaretLineBackgroundColor(
                SettingsManager::Instance()->getSetting(
                    "editor.caretlinecolor").getColorValue());

    //auto indent
    setAutoIndent(
                SettingsManager::Instance()->getSetting(
                    "editor.autoindent").getBoolValue());
    //margin color
    setMarginsBackgroundColor(
                SettingsManager::Instance()->getSetting(
                    "editor.margincolor").getColorValue());
    //show line numbers
    setMarginLineNumbers(1, SettingsManager::Instance()->getSetting(
                                           "editor.showmarginnumbers").getBoolValue());
    setTabWidth(
                SettingsManager::Instance()->getSetting(
                                  "editor.tabwidth").getIntValue());
}



